use Test::More;
use B qw(perlstring);
use ddclient::t;
SKIP: { eval { require Test::Warnings; } or skip($@, 1); }
eval { require 'ddclient'; } or BAIL_OUT($@);


my @valid_ipv6 = (
    "::abcd:efAB:CDEF",  # case sensitivity
    "08:09:0a:0b:0c:0d:0e:0f",  # leading zeros

    # with thanks to http://home.deds.nl/~aeron/regex/valid_ipv6.txt
    "1111:2222:3333:4444:5555:6666:7777:8888",
    "1111:2222:3333:4444:5555:6666:7777::",
    "1111:2222:3333:4444:5555:6666::",
    "1111:2222:3333:4444:5555::",
    "1111:2222:3333:4444::",
    "1111:2222:3333::",
    "1111:2222::",
    "1111::",
    "::",
    "1111:2222:3333:4444:5555:6666::8888",
    "1111:2222:3333:4444:5555::8888",
    "1111:2222:3333:4444::8888",
    "1111:2222:3333::8888",
    "1111:2222::8888",
    "1111::8888",
    "::8888",
    "1111:2222:3333:4444:5555::7777:8888",
    "1111:2222:3333:4444::7777:8888",
    "1111:2222:3333::7777:8888",
    "1111:2222::7777:8888",
    "1111::7777:8888",
    "::7777:8888",
    "1111:2222:3333:4444::6666:7777:8888",
    "1111:2222:3333::6666:7777:8888",
    "1111:2222::6666:7777:8888",
    "1111::6666:7777:8888",
    "::6666:7777:8888",
    "1111:2222:3333::5555:6666:7777:8888",
    "1111:2222::5555:6666:7777:8888",
    "1111::5555:6666:7777:8888",
    "::5555:6666:7777:8888",
    "1111:2222::4444:5555:6666:7777:8888",
    "1111::4444:5555:6666:7777:8888",
    "::4444:5555:6666:7777:8888",
    "1111::3333:4444:5555:6666:7777:8888",
    "::3333:4444:5555:6666:7777:8888",
    "::2222:3333:4444:5555:6666:7777:8888",
    # IPv4-mapped IPv6 addresses
    "1111:2222:3333:4444:5555:6666:0.0.0.0",
    "1111:2222:3333:4444:5555:6666:00.00.00.00",
    "1111:2222:3333:4444:5555:6666:000.000.000.000",
    "1111:2222:3333:4444:5555:6666:123.123.123.123",
    "1111:2222:3333:4444:5555::123.123.123.123",
    "1111:2222:3333:4444::123.123.123.123",
    "1111:2222:3333::123.123.123.123",
    "1111:2222::123.123.123.123",
    "1111::123.123.123.123",
    "::123.123.123.123",
    "1111:2222:3333:4444::6666:123.123.123.123",
    "1111:2222:3333::6666:123.123.123.123",
    "1111:2222::6666:123.123.123.123",
    "1111::6666:123.123.123.123",
    "::6666:123.123.123.123",
    "1111:2222:3333::5555:6666:123.123.123.123",
    "1111:2222::5555:6666:123.123.123.123",
    "1111::5555:6666:123.123.123.123",
    "::5555:6666:123.123.123.123",
    "1111:2222::4444:5555:6666:123.123.123.123",
    "1111::4444:5555:6666:123.123.123.123",
    "::4444:5555:6666:123.123.123.123",
    "1111::3333:4444:5555:6666:123.123.123.123",
    "::3333:4444:5555:6666:123.123.123.123",
    "::2222:3333:4444:5555:6666:123.123.123.123",
);

my @invalid_ipv6 = (
    # Empty string and bogus text
    undef,
    "",
    "   ",
    "foobar",

    # Valid IPv6 with extra text before or after
    "foo2001:DB8:4341:0781:1111:2222:3333:4444",
    "foo 2001:DB8:4341:0781::4444",
    "foo 2001:DB8:4341:0781:1111:: bar",
    "foo2001:DB8:4341:0781::100bar",
    "2001:DB8:4341:0781::1 bar",
    "2001:DB8:4341:0781::0001bar",
    "foo bar 3001:DB8:4341:0781:1111:2222:3333:4444 foo bar",
    "__3001:DB8:4341:0781::4444",
    "__3001:DB8:4341:0781:1111::__",
    "--3001:DB8:4341:0781::100--",
    "/3001:DB8:4341:0781::1/",
    "3001:DB8:4341:0781::0001%",
    "fdb6:1d86:d9bd:1::4444%eth0",
    "fdb6:1d86:d9bd:1:1111::%ens192",
    "fdb6:1d86:d9bd:1::100%en0",
    "fdb6:1d86:d9bd:1::1%eth1.100",

    # With thanks to http://home.deds.nl/~aeron/regex/invalid_ipv6.txt
    # Invalid data
    "XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX",

    # Too many components
    "1111:2222:3333:4444:5555:6666:7777:8888:9999",
    "1111:2222:3333:4444:5555:6666:7777:8888::",
    "::2222:3333:4444:5555:6666:7777:8888:9999",

    # Too few components
    "1111:2222:3333:4444:5555:6666:7777",
    "1111:2222:3333:4444:5555:6666",
    "1111:2222:3333:4444:5555",
    "1111:2222:3333:4444",
    "1111:2222:3333",
    "1111:2222",
    "1111",

    # Missing :
    "11112222:3333:4444:5555:6666:7777:8888",
    "1111:22223333:4444:5555:6666:7777:8888",
    "1111:2222:33334444:5555:6666:7777:8888",
    "1111:2222:3333:44445555:6666:7777:8888",
    "1111:2222:3333:4444:55556666:7777:8888",
    "1111:2222:3333:4444:5555:66667777:8888",
    "1111:2222:3333:4444:5555:6666:77778888",

    # Missing : intended for ::
    "1111:2222:3333:4444:5555:6666:7777:8888:",
    "1111:2222:3333:4444:5555:6666:7777:",
    "1111:2222:3333:4444:5555:6666:",
    "1111:2222:3333:4444:5555:",
    "1111:2222:3333:4444:",
    "1111:2222:3333:",
    "1111:2222:",
    "1111:",
    ":",
    ":8888",
    ":7777:8888",
    ":6666:7777:8888",
    ":5555:6666:7777:8888",
    ":4444:5555:6666:7777:8888",
    ":3333:4444:5555:6666:7777:8888",
    ":2222:3333:4444:5555:6666:7777:8888",
    ":1111:2222:3333:4444:5555:6666:7777:8888",

    # :::
    ":::2222:3333:4444:5555:6666:7777:8888",
    "1111:::3333:4444:5555:6666:7777:8888",
    "1111:2222:::4444:5555:6666:7777:8888",
    "1111:2222:3333:::5555:6666:7777:8888",
    "1111:2222:3333:4444:::6666:7777:8888",
    "1111:2222:3333:4444:5555:::7777:8888",
    "1111:2222:3333:4444:5555:6666:::8888",
    "1111:2222:3333:4444:5555:6666:7777:::",

    # Double ::
    "::2222::4444:5555:6666:7777:8888",
    "::2222:3333::5555:6666:7777:8888",
    "::2222:3333:4444::6666:7777:8888",
    "::2222:3333:4444:5555::7777:8888",
    "::2222:3333:4444:5555:7777::8888",
    "::2222:3333:4444:5555:7777:8888::",

    "1111::3333::5555:6666:7777:8888",
    "1111::3333:4444::6666:7777:8888",
    "1111::3333:4444:5555::7777:8888",
    "1111::3333:4444:5555:6666::8888",
    "1111::3333:4444:5555:6666:7777::",

    "1111:2222::4444::6666:7777:8888",
    "1111:2222::4444:5555::7777:8888",
    "1111:2222::4444:5555:6666::8888",
    "1111:2222::4444:5555:6666:7777::",

    "1111:2222:3333::5555::7777:8888",
    "1111:2222:3333::5555:6666::8888",
    "1111:2222:3333::5555:6666:7777::",

    "1111:2222:3333:4444::6666::8888",
    "1111:2222:3333:4444::6666:7777::",

    "1111:2222:3333:4444:5555::7777::",

    # Invalid data
    "XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:1.2.3.4",
    "1111:2222:3333:4444:5555:6666:256.256.256.256",

    # Too many components
    "1111:2222:3333:4444:5555:6666:7777:8888:1.2.3",
    "1111:2222:3333:4444:5555:6666:7777:1.2.3.4",
    "1111:2222:3333:4444:5555:6666::1.2.3.4",
    "::2222:3333:4444:5555:6666:7777:1.2.3.4",
    "1111:2222:3333:4444:5555:6666:1.2.3.4.5",

    # Too few components
    "1111:2222:3333:4444:5555:1.2.3.4",
    "1111:2222:3333:4444:1.2.3.4",
    "1111:2222:3333:1.2.3.4",
    "1111:2222:1.2.3.4",
    "1111:1.2.3.4",
    "1.2.3.4",

    # Missing :
    "11112222:3333:4444:5555:6666:1.2.3.4",
    "1111:22223333:4444:5555:6666:1.2.3.4",
    "1111:2222:33334444:5555:6666:1.2.3.4",
    "1111:2222:3333:44445555:6666:1.2.3.4",
    "1111:2222:3333:4444:55556666:1.2.3.4",
    "1111:2222:3333:4444:5555:66661.2.3.4",

    # Missing .
    "1111:2222:3333:4444:5555:6666:255255.255.255",
    "1111:2222:3333:4444:5555:6666:255.255255.255",
    "1111:2222:3333:4444:5555:6666:255.255.255255",

    # Missing : intended for ::
    ":1.2.3.4",
    ":6666:1.2.3.4",
    ":5555:6666:1.2.3.4",
    ":4444:5555:6666:1.2.3.4",
    ":3333:4444:5555:6666:1.2.3.4",
    ":2222:3333:4444:5555:6666:1.2.3.4",
    ":1111:2222:3333:4444:5555:6666:1.2.3.4",

    # :::
    ":::2222:3333:4444:5555:6666:1.2.3.4",
    "1111:::3333:4444:5555:6666:1.2.3.4",
    "1111:2222:::4444:5555:6666:1.2.3.4",
    "1111:2222:3333:::5555:6666:1.2.3.4",
    "1111:2222:3333:4444:::6666:1.2.3.4",
    "1111:2222:3333:4444:5555:::1.2.3.4",

    # Double ::
    "::2222::4444:5555:6666:1.2.3.4",
    "::2222:3333::5555:6666:1.2.3.4",
    "::2222:3333:4444::6666:1.2.3.4",
    "::2222:3333:4444:5555::1.2.3.4",

    "1111::3333::5555:6666:1.2.3.4",
    "1111::3333:4444::6666:1.2.3.4",
    "1111::3333:4444:5555::1.2.3.4",

    "1111:2222::4444::6666:1.2.3.4",
    "1111:2222::4444:5555::1.2.3.4",

    "1111:2222:3333::5555::1.2.3.4",

    # Missing parts
    "::.",
    "::..",
    "::...",
    "::1...",
    "::1.2..",
    "::1.2.3.",
    "::.2..",
    "::.2.3.",
    "::.2.3.4",
    "::..3.",
    "::..3.4",
    "::...4",

    # Extra : in front
    ":1111:2222:3333:4444:5555:6666:7777::",
    ":1111:2222:3333:4444:5555:6666::",
    ":1111:2222:3333:4444:5555::",
    ":1111:2222:3333:4444::",
    ":1111:2222:3333::",
    ":1111:2222::",
    ":1111::",
    ":::",
    ":1111:2222:3333:4444:5555:6666::8888",
    ":1111:2222:3333:4444:5555::8888",
    ":1111:2222:3333:4444::8888",
    ":1111:2222:3333::8888",
    ":1111:2222::8888",
    ":1111::8888",
    ":::8888",
    ":1111:2222:3333:4444:5555::7777:8888",
    ":1111:2222:3333:4444::7777:8888",
    ":1111:2222:3333::7777:8888",
    ":1111:2222::7777:8888",
    ":1111::7777:8888",
    ":::7777:8888",
    ":1111:2222:3333:4444::6666:7777:8888",
    ":1111:2222:3333::6666:7777:8888",
    ":1111:2222::6666:7777:8888",
    ":1111::6666:7777:8888",
    ":::6666:7777:8888",
    ":1111:2222:3333::5555:6666:7777:8888",
    ":1111:2222::5555:6666:7777:8888",
    ":1111::5555:6666:7777:8888",
    ":::5555:6666:7777:8888",
    ":1111:2222::4444:5555:6666:7777:8888",
    ":1111::4444:5555:6666:7777:8888",
    ":::4444:5555:6666:7777:8888",
    ":1111::3333:4444:5555:6666:7777:8888",
    ":::3333:4444:5555:6666:7777:8888",
    ":::2222:3333:4444:5555:6666:7777:8888",
    ":1111:2222:3333:4444:5555:6666:1.2.3.4",
    ":1111:2222:3333:4444:5555::1.2.3.4",
    ":1111:2222:3333:4444::1.2.3.4",
    ":1111:2222:3333::1.2.3.4",
    ":1111:2222::1.2.3.4",
    ":1111::1.2.3.4",
    ":::1.2.3.4",
    ":1111:2222:3333:4444::6666:1.2.3.4",
    ":1111:2222:3333::6666:1.2.3.4",
    ":1111:2222::6666:1.2.3.4",
    ":1111::6666:1.2.3.4",
    ":::6666:1.2.3.4",
    ":1111:2222:3333::5555:6666:1.2.3.4",
    ":1111:2222::5555:6666:1.2.3.4",
    ":1111::5555:6666:1.2.3.4",
    ":::5555:6666:1.2.3.4",
    ":1111:2222::4444:5555:6666:1.2.3.4",
    ":1111::4444:5555:6666:1.2.3.4",
    ":::4444:5555:6666:1.2.3.4",
    ":1111::3333:4444:5555:6666:1.2.3.4",
    ":::3333:4444:5555:6666:1.2.3.4",
    ":::2222:3333:4444:5555:6666:1.2.3.4",

    # Extra : at end
    "1111:2222:3333:4444:5555:6666:7777:::",
    "1111:2222:3333:4444:5555:6666:::",
    "1111:2222:3333:4444:5555:::",
    "1111:2222:3333:4444:::",
    "1111:2222:3333:::",
    "1111:2222:::",
    "1111:::",
    ":::",
    "1111:2222:3333:4444:5555:6666::8888:",
    "1111:2222:3333:4444:5555::8888:",
    "1111:2222:3333:4444::8888:",
    "1111:2222:3333::8888:",
    "1111:2222::8888:",
    "1111::8888:",
    "::8888:",
    "1111:2222:3333:4444:5555::7777:8888:",
    "1111:2222:3333:4444::7777:8888:",
    "1111:2222:3333::7777:8888:",
    "1111:2222::7777:8888:",
    "1111::7777:8888:",
    "::7777:8888:",
    "1111:2222:3333:4444::6666:7777:8888:",
    "1111:2222:3333::6666:7777:8888:",
    "1111:2222::6666:7777:8888:",
    "1111::6666:7777:8888:",
    "::6666:7777:8888:",
    "1111:2222:3333::5555:6666:7777:8888:",
    "1111:2222::5555:6666:7777:8888:",
    "1111::5555:6666:7777:8888:",
    "::5555:6666:7777:8888:",
    "1111:2222::4444:5555:6666:7777:8888:",
    "1111::4444:5555:6666:7777:8888:",
    "::4444:5555:6666:7777:8888:",
    "1111::3333:4444:5555:6666:7777:8888:",
    "::3333:4444:5555:6666:7777:8888:",
    "::2222:3333:4444:5555:6666:7777:8888:",
);


subtest "is_ipv6() with valid addresses" => sub {
    foreach my $ip (@valid_ipv6) {
        ok(ddclient::is_ipv6($ip), "is_ipv6('$ip')");
    }
};

subtest "is_ipv6() with invalid addresses" => sub {
    foreach my $ip (@invalid_ipv6) {
        ok(!ddclient::is_ipv6($ip), sprintf("!is_ipv6(%s)", defined($ip) ? "'$ip'" : 'undef'));
    }
};

subtest "is_ipv6() with char adjacent to valid address" => sub {
    foreach my $ch (split(//, '/.,:z @$#&%!^*()_-+'), "\n") {
        subtest perlstring($ch) => sub {
            foreach my $ip (@valid_ipv6) {
                subtest $ip => sub {
                    my $test = $ch . $ip;  # insert at front
                    ok(!ddclient::is_ipv6($test), "!is_ipv6('$test')");
                    $test = $ip . $ch;  # add at end
                    ok(!ddclient::is_ipv6($test), "!is_ipv6('$test')");
                    $test = $ch . $ip . $ch; # wrap front and end
                    ok(!ddclient::is_ipv6($test), "!is_ipv6('$test')");
                };
            }
        };
    }
};

subtest "extract_ipv6()" => sub {
    my @test_cases = (
        {name => "undef",            text => undef,               want => undef},
        {name => "empty",            text => "",                  want => undef},
        {name => "invalid",          text => "::12345",           want => undef},
        {name => "two addrs",        text => "::1\n::2",          want => "::1"},
        {name => "zone index",       text => "fe80::1%0",         want => "fe80::1"},
        {name => "url host+port",    text => "[::1]:123",         want => "::1"},
        {name => "url host+zi+port", text => "[fe80::1%250]:123", want => "fe80::1"},
        {name => "zero pad",         text => "::0001",            want => "::1"},
    );
    foreach my $tc (@test_cases) {
        is(ddclient::extract_ipv6($tc->{text}), $tc->{want}, $tc->{name});
    }
};

subtest "extract_ipv6() of valid addr with adjacent non-word char" => sub {
    foreach my $wb (split(//, '/, @$#&%!^*()_-+'), "\n") {
        subtest perlstring($wb) => sub {
            my $test = "";
            foreach my $ip (@valid_ipv6) {
                $test = "foo" . $wb . $ip . $wb . "bar"; # wrap front and end
                $ip =~ s/\b0+\B//g; ## remove embedded leading zeros for testing
                is(ddclient::extract_ipv6($test), $ip, perlstring($test));
            }
        };
    }
};

subtest "interface config samples" => sub {
    for my $sample (@ddclient::t::interface_samples) {
        if (defined($sample->{want_extract_ipv6_global})) {
            subtest $sample->{name} => sub {
                my $ip = ddclient::extract_ipv6($sample->{text});
                ok(ddclient::is_ipv6($ip), "extract_ipv6() returns an IPv6 address");
            };
            foreach my $line (split(/\n/, $sample->{text})) {
                my $ip = ddclient::extract_ipv6($line);
                if ($ip) {  ## Test cases may have lines that do not contain IPv6 address.
                    ok(ddclient::is_ipv6($ip),
                       sprintf("extract_ipv6(%s) returns an IPv6 address", perlstring($line)));
                }
            }
        }
    }
};

done_testing();
